package com.baoxin.chenxixc4coach.utils;

import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.text.TextUtils;
import android.util.Log;

import com.baoxin.chenxixc4coach.LoginActivity;
import com.baoxin.chenxixc4coach.MyApplication;
import com.baoxin.chenxixc4coach.java.TokenInterceptor;

import org.json.JSONObject;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

import okhttp3.Callback;
import okhttp3.Cookie;
import okhttp3.CookieJar;
import okhttp3.HttpUrl;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;

public class NetUtil {

    private static OkHttpClient client;
    private static String mediaType = "application/json;chartset=utf-8";
    private static final MediaType JSON = MediaType.parse(mediaType);

    public static void get(Context context, String url, Callback callback) throws IOException {

        initClient(context);

        Request request = new Request.Builder()
                .url(url)
                .addHeader("token", MyApplication.getToken())
                .addHeader("accept", mediaType)
                .build();
        client.newCall(request).enqueue(callback);
    }

    public static void get(Context context, String url, Callback callback, int needLogined) throws IOException {

        if (MyApplication.getToken().length() == 0) {
            Intent intent = new Intent(context, LoginActivity.class);
            context.startActivity(intent);
            return;
        }

        initClient(context);

        Request request = new Request.Builder()
                .url(url)
                .addHeader("token", MyApplication.getToken())
                .addHeader("accept", mediaType)
                .build();
        client.newCall(request).enqueue(callback);
    }

    public static void post(Context context, String url, String json, Callback callback) throws IOException {

        initClient(context);

        RequestBody body = RequestBody.create(JSON, json);
        Request request = new Request.Builder()
                .url(url)
                .post(body)
                .addHeader("token", MyApplication.getToken())
                .build();
        client.newCall(request).enqueue(callback);
    }

    public static void post(Context context, String url, String json, Callback callback, int needLogined) throws IOException {
        if (MyApplication.getToken().length() == 0) {
            Intent intent = new Intent(context, LoginActivity.class);
            context.startActivity(intent);
            return;
        }

        initClient(context);

        RequestBody body = RequestBody.create(JSON, json);
        Request request = new Request.Builder()
                .url(url)
                .post(body)
                .addHeader("token", MyApplication.getToken())
                .build();
        client.newCall(request).enqueue(callback);
    }

    /**
     * 同步登录获取newToken(其实每次登录时都会获取新的token，如果长时间未重新登录，token过期，拦截器会调用
     * 此同步登录方法获取新的token)
     *
     * @param url
     * @param userName
     * @param password
     * @return
     * @throws Exception
     */
    public static String getNewToken(String url, String userName, String password) throws Exception {
        JSONObject body = new JSONObject();
        body.put("phone", userName);
        body.put("password", password);
        body.put("type", 0);

        RequestBody body1 = RequestBody.create(JSON, body.toString());
        OkHttpClient client = new OkHttpClient();
        Request request = new Request.Builder()
                .url(url)
                .post(body1)
                .build();

        Response response = client.newCall(request).execute();
        String result = response.body().string();

        JSONObject jsonObject = new JSONObject(result);
        String code = jsonObject.getString("code");
        if (code.equals("0")) {
            String token = jsonObject.getString("token");
            MyApplication.saveToken(token);
            return token;
        } else {//登录过后，token过期，账号密码变更，才会走到这，应该重新登录
            return "";
        }
    }

    private static void initClient(Context context) {
        if (null == client) {
            client = new OkHttpClient.Builder()
                    .connectTimeout(5, TimeUnit.SECONDS)
                    .writeTimeout(5, TimeUnit.SECONDS)
                    .readTimeout(5, TimeUnit.SECONDS)
                  //  .addInterceptor(new TokenInterceptor())
                    .cookieJar(new CookiesManager(context))
                    .build();
        }
    }


    /**
     * 自动管理Cookies
     */
    private static class CookiesManager implements CookieJar {
        private final PersistentCookieStore cookieStore;

        public CookiesManager(Context context) {
            cookieStore = new PersistentCookieStore(context);
        }

        @Override
        public void saveFromResponse(HttpUrl url, List<Cookie> cookies) {
            if (cookies != null && cookies.size() > 0) {
                for (Cookie item : cookies) {
                    cookieStore.add(url, item);
                }
            }
        }

        @Override
        public List<Cookie> loadForRequest(HttpUrl url) {
            List<Cookie> cookies = cookieStore.get(url);
            return cookies;
        }
    }


    public static class SerializableOkHttpCookies implements Serializable {

        private transient final Cookie cookies;
        private transient Cookie clientCookies;

        public SerializableOkHttpCookies(Cookie cookies) {
            this.cookies = cookies;
        }

        public Cookie getCookies() {
            Cookie bestCookies = cookies;
            if (clientCookies != null) {
                bestCookies = clientCookies;
            }
            return bestCookies;
        }

        private void writeObject(ObjectOutputStream out) throws IOException {
            out.writeObject(cookies.name());
            out.writeObject(cookies.value());
            out.writeLong(cookies.expiresAt());
            out.writeObject(cookies.domain());
            out.writeObject(cookies.path());
            out.writeBoolean(cookies.secure());
            out.writeBoolean(cookies.httpOnly());
            out.writeBoolean(cookies.hostOnly());
            out.writeBoolean(cookies.persistent());
        }

        private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
            String name = (String) in.readObject();
            String value = (String) in.readObject();
            long expiresAt = in.readLong();
            String domain = (String) in.readObject();
            String path = (String) in.readObject();
            boolean secure = in.readBoolean();
            boolean httpOnly = in.readBoolean();
            boolean hostOnly = in.readBoolean();
            boolean persistent = in.readBoolean();
            Cookie.Builder builder = new Cookie.Builder();
            builder = builder.name(name);
            builder = builder.value(value);
            builder = builder.expiresAt(expiresAt);
            builder = hostOnly ? builder.hostOnlyDomain(domain) : builder.domain(domain);
            builder = builder.path(path);
            builder = secure ? builder.secure() : builder;
            builder = httpOnly ? builder.httpOnly() : builder;
            clientCookies = builder.build();
        }
    }

    public static class PersistentCookieStore {
        private static final String LOG_TAG = "PersistentCookieStore";
        private static final String COOKIE_PREFS = "Cookies_Prefs";

        private final Map<String, ConcurrentHashMap<String, Cookie>> cookies;
        private final SharedPreferences cookiePrefs;


        public PersistentCookieStore(Context context) {
            cookiePrefs = context.getSharedPreferences(COOKIE_PREFS, 0);
            cookies = new HashMap<>();

            //将持久化的cookies缓存到内存中 即map cookies
            Map<String, ?> prefsMap = cookiePrefs.getAll();
            for (Map.Entry<String, ?> entry : prefsMap.entrySet()) {
                String[] cookieNames = TextUtils.split((String) entry.getValue(), ",");
                for (String name : cookieNames) {
                    String encodedCookie = cookiePrefs.getString(name, null);
                    if (encodedCookie != null) {
                        Cookie decodedCookie = decodeCookie(encodedCookie);
                        if (decodedCookie != null) {
                            if (!cookies.containsKey(entry.getKey())) {
                                cookies.put(entry.getKey(), new ConcurrentHashMap<String, Cookie>());
                            }
                            cookies.get(entry.getKey()).put(name, decodedCookie);
                        }
                    }
                }
            }
        }

        protected String getCookieToken(Cookie cookie) {
            return cookie.name() + "@" + cookie.domain();
        }

        public void add(HttpUrl url, Cookie cookie) {
            String name = getCookieToken(cookie);

            //将cookies缓存到内存中 如果缓存过期 就重置此cookie
            if (!cookie.persistent()) {
                if (!cookies.containsKey(url.host())) {
                    cookies.put(url.host(), new ConcurrentHashMap<String, Cookie>());
                }
                cookies.get(url.host()).put(name, cookie);
            } else {
                if (cookies.containsKey(url.host())) {
                    cookies.get(url.host()).remove(name);
                }
            }

            //将cookies持久化到本地
            SharedPreferences.Editor prefsWriter = cookiePrefs.edit();
            prefsWriter.putString(url.host(), TextUtils.join(",", cookies.get(url.host()).keySet()));
            prefsWriter.putString(name, encodeCookie(new SerializableOkHttpCookies(cookie)));
            prefsWriter.apply();
        }

        public List<Cookie> get(HttpUrl url) {
            ArrayList<Cookie> ret = new ArrayList<>();
            if (cookies.containsKey(url.host()))
                ret.addAll(cookies.get(url.host()).values());
            return ret;
        }

        public boolean removeAll() {
            SharedPreferences.Editor prefsWriter = cookiePrefs.edit();
            prefsWriter.clear();
            prefsWriter.apply();
            cookies.clear();
            return true;
        }

        public boolean remove(HttpUrl url, Cookie cookie) {
            String name = getCookieToken(cookie);

            if (cookies.containsKey(url.host()) && cookies.get(url.host()).containsKey(name)) {
                cookies.get(url.host()).remove(name);

                SharedPreferences.Editor prefsWriter = cookiePrefs.edit();
                if (cookiePrefs.contains(name)) {
                    prefsWriter.remove(name);
                }
                prefsWriter.putString(url.host(), TextUtils.join(",", cookies.get(url.host()).keySet()));
                prefsWriter.apply();

                return true;
            } else {
                return false;
            }
        }

        public List<Cookie> getCookies() {
            ArrayList<Cookie> ret = new ArrayList<>();
            for (String key : cookies.keySet())
                ret.addAll(cookies.get(key).values());

            return ret;
        }

        /**
         * cookies 序列化成 string
         *
         * @param cookie 要序列化的cookie
         * @return 序列化之后的string
         */
        protected String encodeCookie(SerializableOkHttpCookies cookie) {
            if (cookie == null)
                return null;
            ByteArrayOutputStream os = new ByteArrayOutputStream();
            try {
                ObjectOutputStream outputStream = new ObjectOutputStream(os);
                outputStream.writeObject(cookie);
            } catch (IOException e) {
                Log.d(LOG_TAG, "IOException in encodeCookie", e);
                return null;
            }

            return byteArrayToHexString(os.toByteArray());
        }

        /**
         * 将字符串反序列化成cookies
         *
         * @param cookieString cookies string
         * @return cookie object
         */
        protected Cookie decodeCookie(String cookieString) {
            byte[] bytes = hexStringToByteArray(cookieString);
            ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
            Cookie cookie = null;
            try {
                ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
                cookie = ((SerializableOkHttpCookies) objectInputStream.readObject()).getCookies();
            } catch (IOException e) {
                Log.d(LOG_TAG, "IOException in decodeCookie", e);
            } catch (ClassNotFoundException e) {
                Log.d(LOG_TAG, "ClassNotFoundException in decodeCookie", e);
            }

            return cookie;
        }

        /**
         * 二进制数组转十六进制字符串
         *
         * @param bytes byte array to be converted
         * @return string containing hex values
         */
        protected String byteArrayToHexString(byte[] bytes) {
            StringBuilder sb = new StringBuilder(bytes.length * 2);
            for (byte element : bytes) {
                int v = element & 0xff;
                if (v < 16) {
                    sb.append('0');
                }
                sb.append(Integer.toHexString(v));
            }
            return sb.toString().toUpperCase(Locale.US);
        }

        /**
         * 十六进制字符串转二进制数组
         *
         * @param hexString string of hex-encoded values
         * @return decoded byte array
         */
        protected byte[] hexStringToByteArray(String hexString) {
            int len = hexString.length();
            byte[] data = new byte[len / 2];
            for (int i = 0; i < len; i += 2) {
                data[i / 2] = (byte) ((Character.digit(hexString.charAt(i), 16) << 4) + Character.digit(hexString.charAt(i + 1), 16));
            }
            return data;
        }
    }
}
